package house_client

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"github.com/hyperledger/fabric-gateway/pkg/client"
	"github.com/hyperledger/fabric-protos-go-apiv2/gateway"
	"google.golang.org/grpc/status"
)

// House 结构表示房屋信息
type House struct {
	ID            string `json:"id"`       //主键
	Owner         string `json:"owner"`    //主人
	HousePrice    string `json:"price"`    //房屋价格
	HouseArea     string `json:"area"`     //房屋面积
	HouseImages   string `json:"images"`   //房屋图片
	HouseVerified string `json:"verified"` //房屋状态验证
}

var assetId = uuid.New().String()

// Evaluate a transaction to query ledger state.
func GetAllHouses() (*[]House, error) {
	// 创建链接
	houseClient, err := InitHouseClient()
	if err != nil {
		return nil, err
	}

	evaluateResult, err := houseClient.EvaluateTransaction("GetAllHouseAssets")
	if err != nil {
		return nil, errors.New("合同获取失败: " + err.Error())
	}
	var contracts *[]House
	err = json.Unmarshal(evaluateResult, &contracts)
	if err != nil {
		return nil, err
	}
	return contracts, nil
}

// Submit a transaction synchronously, blocking until it has been committed to the ledger.
func CreateHouseAsset(owner, housePrice, houseArea, houseImages, houseVerified string) (string, error) {
	// 创建链接
	houseClient, err := InitHouseClient()
	if err != nil {
		return "", err
	}
	_, err = houseClient.SubmitTransaction("CreateHouseAsset", assetId, owner, housePrice, houseArea, houseImages, houseVerified)
	if err != nil {
		return "", errors.New("添加合同失败: " + err.Error())
	}
	fmt.Println("*** 合同提交成功 ***")
	return "*** 合同提交成功 ***，合同编号为: " + assetId + ";请妥善保管该编号", nil
}

// Evaluate a transaction by assetID to query ledger state.
func ReadHouseAssetById(houseId string) (*House, error) {
	// 创建链接
	houseClient, err := InitHouseClient()
	if err != nil {
		return nil, err
	}

	evaluateResult, err := houseClient.EvaluateTransaction("ReadHouseAsset", houseId)
	if err != nil {
		return nil, errors.New("房屋信息获取失败: " + err.Error())
	}
	var asset *House
	err = json.Unmarshal(evaluateResult, &asset)
	if err != nil {
		return nil, errors.New("转换失败")
	}
	return asset, nil
}

// Submit transaction asynchronously, blocking until the transaction has been sent to the orderer, and allowing
// this thread to process the chaincode response (e.g. update a UI) without waiting for the commit notification
func TransferHouseAsset() error {
	houseClient, err := InitHouseClient()
	if err != nil {
		return err
	}

	submitResult, commit, err := houseClient.SubmitAsync("transferAsset", client.WithArguments(assetId, "Mark"))
	if err != nil {
		panic(fmt.Errorf("failed to submit transaction asynchronously: %w", err))
	}

	fmt.Printf("\n*** Successfully submitted transaction to transfer ownership from %s to Mark. \n", string(submitResult))
	fmt.Println("*** Waiting for transaction commit.")

	if commitStatus, err := commit.Status(); err != nil {
		panic(fmt.Errorf("failed to get commit status: %w", err))
	} else if !commitStatus.Successful {
		panic(fmt.Errorf("transaction %s failed to commit with status: %d", commitStatus.TransactionID, int32(commitStatus.Code)))
	}

	fmt.Printf("*** Transaction committed successfully\n")
	return nil
}

// Submit transaction, passing in the wrong number of arguments ,expected to throw an error containing details of any error responses from the smart contract.
func ExampleErrorHandling() error {
	houseClient, err := InitHouseClient()
	if err != nil {
		return err
	}

	_, err = houseClient.SubmitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300")
	if err == nil {
		panic("******** FAILED to return an error")
	}

	fmt.Println("*** Successfully caught the error:")

	switch err := err.(type) {
	case *client.EndorseError:
		fmt.Printf("Endorse error for transaction %s with gRPC status %v: %s\n", err.TransactionID, status.Code(err), err)
	case *client.SubmitError:
		fmt.Printf("Submit error for transaction %s with gRPC status %v: %s\n", err.TransactionID, status.Code(err), err)
	case *client.CommitStatusError:
		if errors.Is(err, context.DeadlineExceeded) {
			fmt.Printf("Timeout waiting for transaction %s commit status: %s", err.TransactionID, err)
		} else {
			fmt.Printf("Error obtaining commit status for transaction %s with gRPC status %v: %s\n", err.TransactionID, status.Code(err), err)
		}
	case *client.CommitError:
		fmt.Printf("Transaction %s failed to commit with status %d: %s\n", err.TransactionID, int32(err.Code), err)
	default:
		panic(fmt.Errorf("unexpected error type %T: %w", err, err))
	}

	// Any error that originates from a peer or orderer node external to the gateway will have its details
	// embedded within the gRPC status error. The following code shows how to extract that.
	statusErr := status.Convert(err)

	details := statusErr.Details()
	if len(details) > 0 {
		fmt.Println("Error Details:")

		for _, detail := range details {
			switch detail := detail.(type) {
			case *gateway.ErrorDetail:
				fmt.Printf("- address: %s, mspId: %s, message: %s\n", detail.Address, detail.MspId, detail.Message)
			}
		}
	}
	return nil
}
